package com.sl.core.engine.plugin.loader.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.core.util.ZipUtil;
import com.sl.core.engine.plugin.domain.PluginDecorate;
import com.sl.core.engine.plugin.loader.ProcessPluginLoader;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

@Slf4j
public class VmProcessPluginLoader extends AbstractProcessPluginLoader implements ProcessPluginLoader {

    public static VmProcessPluginLoader INSTANCE = new VmProcessPluginLoader();

    private Map<String, List<PluginDecorate>> pluginStoreMap = new HashMap<>();

    @Override
    public Map<String, List<PluginDecorate>> loader() {
        if (MapUtil.isEmpty(this.pluginStoreMap)) {
            Map<String, List<PluginDecorate>> loader = super.loader(this.getClass().getClassLoader());
            for (Map.Entry<String, List<PluginDecorate>> stringListEntry : loader.entrySet()) {
                for (PluginDecorate pluginDecorate : stringListEntry.getValue()) {
                    pluginDecorate.setSystemFlag(true);
                }
            }
            this.pluginStoreMap = loader;
        }

        Map<String, List<PluginDecorate>> pm = getPluginStoreMap(pluginStoreMap);

        return pm;
    }

    @Override
    public List<PluginDecorate> get(String code) {
        return this.pluginStoreMap.get(code);
    }

    @Override
    public PluginDecorate get(String orgId, String code, String version) {
        PluginDecorate pluginDecorate = null;
        for (Map.Entry<String, List<PluginDecorate>> entry : this.pluginStoreMap.entrySet()) {
            String k = entry.getKey();
            List<PluginDecorate> v = entry.getValue();
            if (k.equals(code)) {
                if (StrUtil.isBlank(version)) {
                    pluginDecorate = v.stream().findFirst().orElse(null);
                } else {
                    String finalVersion = version;
                    pluginDecorate = v.stream().filter(vv -> vv.getVersion().equals(finalVersion)).findFirst().orElse(null);
                }
                break;
            }
        }
        return pluginDecorate;
    }

    @SneakyThrows
    @Override
    public ByteArrayInputStream getResourceCode(String orgId, String pluginCode, String pluginVersion) {

        PluginDecorate res = null;
        //从代码里面获取
        Map<String, List<PluginDecorate>> loader = super.loader(this.getClass().getClassLoader());

        for (Map.Entry<String, List<PluginDecorate>> stringListEntry : loader.entrySet()) {

            List<PluginDecorate> value = stringListEntry.getValue();
            res = value.stream().filter(v -> StrUtil.equalsAnyIgnoreCase(v.getPluginCode(), pluginCode)).findFirst().orElse(null);

            if (ObjectUtil.isNotNull(res)) {
                break;
            }
        }

        if (ObjectUtil.isNull(res)) {
            return null;
        }

        String fileUrl = res.getFileUrl();
        fileUrl = StrUtil.replace(fileUrl,"%20"," ");

        File mkdir = null;
        if (isJarFile(fileUrl)) {
            log.info("这是一个jarFile,进行处理");
            URL urled = URLUtil.url(fileUrl);
            JarURLConnection urlConnection = (JarURLConnection) urled.openConnection();
            JarFile jarFile = urlConnection.getJarFile();
            File tmpDir = FileUtil.file(System.getProperty("user.home"));

            tmpDir = FileUtil.mkdir(new File(tmpDir.getAbsolutePath(), "unpack"));

            String projectName = jarFile.getName();
            log.info("projectName {}", projectName);
            String name = FileUtil.getPrefix(projectName);

            name = replaceVersion(name);

            log.info("fileName {}", name);
            mkdir = FileUtil.mkdir(new File(tmpDir.getAbsolutePath(), name));
            log.info("tempProjectName {}", mkdir.getName());

            unpackJar(jarFile, mkdir);
            log.info("解压成功 {}", mkdir.getAbsolutePath());
        } else {
            File project = FileUtil.file(fileUrl);
            mkdir = findProject(project);
        }

        ByteArrayInputStream fileInputStream = getFileInputStream(mkdir);
        if (isJarFile(fileUrl)) {
            FileUtil.del(mkdir);
        }
        return fileInputStream;


    }

    private String replaceVersion(String fileName) {

        List<String> split = StrUtil.split(fileName, "-");

        StringJoiner stringJoiner = new StringJoiner("-");
        for (String s : split) {
            if (s.contains(".")) {
                break;
            }
            stringJoiner.add(s);
        }
        return stringJoiner.toString();
    }

    public static void unpackJar(JarFile jar, File parentFile) throws IOException {

        String destinationDir = parentFile.getAbsolutePath();
//        List<String> classPathList = new ArrayList<>();
        for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) {
            JarEntry entry = (JarEntry) enums.nextElement();
            String fileName = destinationDir + File.separator + entry.getName();
            File f = new File(fileName);
            if (fileName.endsWith("/")) {
                f.mkdirs();
            }
//            classPathList.add(fileName);
        }

        for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements(); ) {
            JarEntry entry = (JarEntry) enums.nextElement();
            String fileName = destinationDir + File.separator + entry.getName();
            File f = new File(fileName);
            if (!fileName.endsWith("/")) {
                InputStream is = jar.getInputStream(entry);
                FileOutputStream fos = new FileOutputStream(f);
                while (is.available() > 0) {
                    fos.write(is.read());
                }
                fos.close();
                is.close();
            }
//            classPathList.add(fileName);
        }

    }

    private ByteArrayInputStream getFileInputStream(File project) {

        String projectRootPath = project.getAbsolutePath();
        String projectName = project.getName();

        List<File> childrenFiles = FileUtil.loopFiles(project, (v) -> {

            String absolutePath = v.getAbsolutePath();
            String suffix = FileUtil.getSuffix(v.getName());
            String prefix = FileUtil.getPrefix(v.getName());

            if (StrUtil.contains(absolutePath, "META-INF")) {
                return false;
            }
            return StrUtil.equalsAnyIgnoreCase(suffix, "java", ".css", "js", "html", "properties");
        });


        File tmpDir = FileUtil.getTmpDir();
        File projectDir = FileUtil.mkdir(new File(tmpDir.getAbsolutePath(), projectName));
        File mkdir = FileUtil.mkdir(new File(projectDir.getAbsolutePath(), projectName));
        for (File childrenFile : childrenFiles) {
            String absolutePath = childrenFile.getAbsolutePath();
            String replace = StrUtil.replace(absolutePath, projectRootPath, "");
            String r = replaceTargetAndClass(replace);
            if (StrUtil.contains(r, "/src/main")) {
                continue;
            }
//            File files = createFileDir(r, mkdir);
            File tpFile = FileUtil.file(mkdir, r);
//            File tpFile = FileUtil.checkSlip(mkdir, buildFile(mkdir, r));
            FileUtil.writeBytes(FileUtil.readBytes(childrenFile), tpFile);
        }
        //整一个fine

        //data:application/zip;
        File zip = ZipUtil.zip(projectDir, Charset.defaultCharset());

        byte[] bytes = IoUtil.readBytes(FileUtil.getInputStream(zip));

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        FileUtil.del(projectDir);
        return byteArrayInputStream;
    }

    private Boolean isJarFile(String fileUrl) {
        URL urled = URLUtil.url(fileUrl);
        return StrUtil.equals(urled.getProtocol(), "jar");

    }


//    private File createFileDir(String path, File mkdir) {
//        List<String> split = StrUtil.split(path, File.separator);
//        if (CollUtil.isEmpty(split)) {
//            return FileUtil.file(mkdir, path);
//        }
//
//        Integer i = 0;
//        String s = CollUtil.get(split, i);
//        if (StrUtil.isBlank(s)) {
//            i++;
//            s = CollUtil.get(split, i);
//        }
//        if (StrUtil.isNotBlank(FileUtil.getSuffix(s))) {
//            return FileUtil.file(mkdir, s);
//        } else {
//            String collect = split.stream().skip(i + 1).collect(Collectors.joining(File.separator));
//            File f = new File(mkdir.getAbsolutePath(), s);
//            if (!FileUtil.exist(f)) {
//                f = FileUtil.mkdir(new File(mkdir.getAbsolutePath(), s));
//            }
//            return createFileDir(collect, f);
//        }
//    }

    private String replaceTargetAndClass(String path) {

        path = StrUtil.replace(path, "/target", "");
        path = StrUtil.replace(path, "/classes", "");
        return path;
    }

    private File findProject(File file) {
        File parent = FileUtil.getParent(file, 1);
        String projectName = parent.getName();

        if (StrUtil.equalsAnyIgnoreCase(projectName, "classes", "target")) {
            return findProject(parent);
        }

        return parent;
    }
}
